package main

import (
	pb "complex_go_server_grpc/proto"
	"context"
	"flag"
	"io"
	"log"
	"math/rand"
	"time"

	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
	"google.golang.org/grpc/credentials/insecure"
)

var (
	tls                = flag.Bool("tls", false, "Connection uses TLS if true, else plain TCP")
	caFile             = flag.String("ca_file", "", "The file containing the CA root cert file")
	serverAddr         = flag.String("addr", "localhost:50051", "The server address in the format of host:port")
	serverHostOverride = flag.String("server_host_override", "x.test.example.com", "The server name used to verify the hostname returned by the TLS handshake")
)

// 获取给定点的特征。
func printFeature(client pb.RouteGuideClient, point *pb.Point) {
	// 打印日志，获取给定点的特征
	log.Printf("获取点 (%d, %d) 的特征", point.Latitude, point.Longitude)
	// 创建带有超时的上下文
	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()
	// 调用 gRPC 客户端的 GetFeature 方法获取特征
	feature, err := client.GetFeature(ctx, point)
	if err != nil {
		// 处理获取特征失败的情况
		log.Fatalf("client.GetFeature 失败：%v", err)
	}
	// 打印获取到的特征
	log.Println(feature)
}

// 列出在给定边界矩形内的所有特征。
func printFeatures(client pb.RouteGuideClient, rect *pb.Rectangle) {
	log.Printf("Looking for features within %v", rect)
	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()
	stream, err := client.ListFeatures(ctx, rect)
	if err != nil {
		log.Fatalf("client.ListFeatures failed: %v", err)
	}
	for {
		feature, err := stream.Recv()
		if err == io.EOF {
			break
		}
		if err != nil {
			log.Fatalf("client.ListFeatures failed: %v", err)
		}
		log.Printf("Feature: name: %q, point:(%v, %v)", feature.GetName(),
			feature.GetLocation().GetLatitude(), feature.GetLocation().GetLongitude())
	}
}

// runRecordRoute sends a sequence of points to server and expects to get a RouteSummary from server.
func runRecordRoute(client pb.RouteGuideClient) {
	// 随机生成一系列的点
	r := rand.New(rand.NewSource(time.Now().UnixNano()))
	pointCount := int(r.Int31n(100)) + 2 // Traverse at least two points
	var points []*pb.Point
	for i := 0; i < pointCount; i++ {
		points = append(points, randomPoint(r))
	}
	log.Printf("Traversing %d points.", len(points))
	//============开始发送点流==============
	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()
	stream, err := client.RecordRoute(ctx)
	if err != nil {
		log.Fatalf("client.RecordRoute failed: %v", err)
	}
	for _, point := range points {
		//遍历点并一个一个发送过去
		if err := stream.Send(point); err != nil {
			log.Fatalf("client.RecordRoute: stream.Send(%v) failed: %v", point, err)
		}
	}
	//等待结束并关闭
	reply, err := stream.CloseAndRecv()
	if err != nil {
		log.Fatalf("client.RecordRoute failed: %v", err)
	}
	log.Printf("Route summary: %v", reply)
}

// runRouteChat 接收一系列路由信息，同时为不同的位置发送信息。
func runRouteChat(client pb.RouteGuideClient) {
	// 预定义一组路由信息
	notes := []*pb.RouteNote{
		{Location: &pb.Point{Latitude: 0, Longitude: 1}, Message: "First message"},
		{Location: &pb.Point{Latitude: 0, Longitude: 2}, Message: "Second message"},
		{Location: &pb.Point{Latitude: 0, Longitude: 3}, Message: "Third message"},
		{Location: &pb.Point{Latitude: 0, Longitude: 1}, Message: "Fourth message"},
		{Location: &pb.Point{Latitude: 0, Longitude: 2}, Message: "Fifth message"},
		{Location: &pb.Point{Latitude: 0, Longitude: 3}, Message: "Sixth message"},
	}
	// 创建一个带有超时的上下文
	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()
	// 使用 RouteChat 方法创建流式 RPC 客户端
	stream, err := client.RouteChat(ctx)
	if err != nil {
		log.Fatalf("client.RouteChat 失败：%v", err)
	}
	// 创建一个等待信号的通道
	waitc := make(chan struct{})
	// 启动协程监听流式响应
	go func() {
		for {
			in, err := stream.Recv()
			// 如果流结束，关闭等待信号通道并返回
			if err == io.EOF {
				close(waitc)
				return
			}
			// 处理接收流过程中的错误
			if err != nil {
				log.Fatalf("client.RouteChat 失败：%v", err)
			}
			// 打印接收到的消息和位置
			log.Printf("收到消息 %s 在点(%d, %d)", in.Message, in.Location.Latitude, in.Location.Longitude)
		}
	}()
	// 遍历预定义的注释并通过流发送
	for _, note := range notes {
		if err := stream.Send(note); err != nil {
			log.Fatalf("client.RouteChat: stream.Send(%v) 失败：%v", note, err)
		}
	}

	// 关闭发送流
	stream.CloseSend()

	// 等待流结束的信号
	<-waitc
}

func main() {
	flag.Parse()
	var opts []grpc.DialOption
	if *tls {
		if *caFile == "" {
			//*caFile = data.Path("x509/ca_cert.pem")
		}
		creds, err := credentials.NewClientTLSFromFile(*caFile, *serverHostOverride)
		if err != nil {
			log.Fatalf("Failed to create TLS credentials: %v", err)
		}
		opts = append(opts, grpc.WithTransportCredentials(creds))
	} else {
		opts = append(opts, grpc.WithTransportCredentials(insecure.NewCredentials()))
	}
	//建立连接
	conn, err := grpc.Dial(*serverAddr, opts...)
	if err != nil {
		log.Fatalf("fail to dial: %v", err)
	}
	defer conn.Close()
	//根据连接创建客户端
	client := pb.NewRouteGuideClient(conn)
	//========利用客户端进行调用=========
	// 查看一个有效的点
	//printFeature(client, &pb.Point{Latitude: 409146138, Longitude: -746188906})
	// 查看一个无效的点
	//printFeature(client, &pb.Point{Latitude: 0, Longitude: 0})
	// Looking for features between 40, -75 and 42, -73.
	// printFeatures(client, &pb.Rectangle{
	// 	Lo: &pb.Point{Latitude: 400000000, Longitude: -750000000},
	// 	Hi: &pb.Point{Latitude: 420000000, Longitude: -730000000},
	// })

	// RecordRoute
	//runRecordRoute(client)

	// RouteChat
	runRouteChat(client)
}

// =====辅助功能=====
func randomPoint(r *rand.Rand) *pb.Point {
	lat := (r.Int31n(180) - 90) * 1e7
	long := (r.Int31n(360) - 180) * 1e7
	return &pb.Point{Latitude: lat, Longitude: long}
}
